本文主要为GAMES201高质量实时渲染LEC-02的NOTES
Lecture 2 Recap of CG Basics
2-1 Graphics(Hardware) Pipeline 渲染管线
渲染管线的定义与分类
- 渲染管线:渲染管线实际上就是渲染过程流水线,指的不是具体的某一样东西,而是一个流程。实际上是将一个object转变成一张image展示在屏幕上的过程
- 分类: 可编程渲染管线与固定管线(已抛弃)
固定渲染管线(已抛弃) |
可编程渲染管线 |
即可配置管线,类似于电路开关,效果组合繁琐 |
效果和组合使用编程实现,自由度高 |
OpenGL 3.0+,Direct3D 10+.OpenGL ES 2+,WebGL已经淘汰固定渲染管线 |
灵活易使用 |
图2-1 渲染管线示意图
渲染管线模块划分(按照虎书划分)
Part1:Vertex Processing(顶点处理):信息准备
- Vertex Processing阶段也称为几何阶段,主要是为光栅化阶段做准备,输入为顶点坐标,经过modeling,viewing,projection transformations,剪裁,然后将坐标映射到屏幕空间上
- 本阶段也将顶点的颜色,表面法线,对应材质的uv坐标传入到光栅化阶段
图2-2 Vertex Processing
1 2 3 4 5 6
| 1. 顶点着色器 输入的每个顶点都会调用一次顶点着色器,主要完成坐标变换(如改变顶点位置模拟水面,布料等),逐顶点光照。并输出后续阶段所需要的数据 2. 曲面细分着色器:它是可以选择的,主要是用于细分图元 3. 几何着色器: 用于执行逐图元操作,用于产生更多的图元 4. 剪裁: 将不在摄像机视野的顶点剪裁掉,并剔除某些三角图元的曲面 5. 屏幕映射: 将每个图元的x和y坐标转换到屏幕坐标系
|
Part2:Rasterization(光栅化):信息与像素关联,将像素封装成为片元
- 光栅化为渲染管线核心阶段,通过将Vertex Processing阶段获取到的顶点坐标对应的屏幕坐标,将它们两两互连,可以得到整个模型由一个个图元组成,然后在以这些基础图元进行操作
- 光栅化的任务是决定每个渲染图元中哪些像素绘制在屏幕上,需要对上一个阶段得到的逐顶点数据进行差值,然后进行逐像素处理
- 关于光栅化详细内容可以参考《Fundamentals Of Computer Graphics》8.1内容
图2-3 Rasterization
1 2 3 4 5 6 7 8
| 1. 三角形设置 该阶段计算光栅化一个三角形网格需要的信息。具体来说,上个阶段输出过来的都是三角网格的顶点,即三角网格每条边的两个端点。但如果要得到整个三角网格对像素的覆盖情况,我们就必须计算每条边上的像素坐标。为了能够计算边界像素的坐标信息,我们就需要得到三角形边界的表示方法。这就是一个计算三角网格表示数据的过程。 2. 三角形覆盖 该阶段会检查每个像素是否被一个三角网格覆盖,如果被覆盖就会生成一个片元。该阶段会根据上一阶段的计算结果判断一个三角网格覆盖了哪些像素,并用三角网格的三个顶点的信息对覆盖区域的像素进行插值。这一阶段会输出一个片元序列。 3. 片元着色器 顾名思义,给每个像素着色。该阶段可由开发者编程实现一些渲染效果,并完成很多重要的渲染技术,如纹理采样等。片元着色器仅能影响单个片元。 4. 逐片元操作 在DriectX中又称为输出合并阶段
|
Part3:Fragment Processing:片元处理 && Part4:Blending(混合阶段):片元混合
- 光栅化阶段结束之后,会得到片元序列,这些片元序列并不只是简单的像素点,而是状态的集合,一个片元包含的信息主要有:
屏幕坐标 |
深度信息 |
顶点信息 |
法线信息 |
纹理坐标 |
颜色信息 |
* 对于一个像素点,可能会对应着多个片元,因此需要决定,这个像素该由哪个片元所拥有,又或者是多个片元共同拥有,即混合。 |
|
|
|
|
|
- 在这一阶段需要片元需要经过许多测试决定其能否出现在屏幕上,通过这个阶段可以实现很多的效果,例如透明,阴影渲染,轮廓渲染等。
图2-4 Fragment Processing && Blending
2-2 Topics Two: OpenGL
OpenGL基本介绍
- OpenGL是一系列API,其在CPU端执行,负责调度GPU,与语言无关系,跨平台的
- OpenGL版本是碎片化的.相比于DirectX有很大不同,OpenGL是C风格代买不好使用,早期甚至无法Debug
OpenGL的使用
图2-5 渲染过程类比油画
A.Place objects/models
1 2 3 4 5 6 7
| * Model specification * Model transformation * 顶点缓冲对象,VBO(Vertex Buffer Objects) * 顶点缓冲对象VBO是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。 * 可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。 * VBO与.obj文件十分相似 * 使用OpenGL函数获取矩阵:glTranslate,glMultMatrix,etc
|
B.Set up an easel,放置画架
1 2
| * 视图变换:放置相机,e,g,gluPerspective * OpenGL中的画架:framebuffer
|
C.Attach a canvas to the easel,固定画布
1 2
| * 同一个画架换画布来画不同的图,即指定一个framebuffer之后可以渲染出很多不同的textures * 双重缓冲,垂直同步,三重缓冲
|
D.Paint to the canvas
1 2 3 4 5 6
| * i,e.,画画就是在做shading * 本系列只会用到顶点像素着色 * For each vertex in parallel:OpenGL调用vertex shader(MVP:ModelView,Projection):Transform vertex,other ops * 将三角形打成片段 * 将每个片段着色,使用片段着色器 * OpenGL进行深度测试
|
E.Summary
1 2 3 4
| * Specify objects,camera,MVP,etc * Specify framebuffer and i/o textures * Specify vertex/fragment shaders * Render!
|
2-3 OpenGL Shading Language(GLSL)
什么是着色语言(Shader language)
- 着色语言定义顶点与片段的着色的做法,着色语言使用起来与c语言相似,早期时在GPU使用汇编语言,后来产生了着色语言
- 当前主流着色语言:HLSL(在DirectX上:vertex+pixel)
- GLSL in OpenGL(vertex+fragment)
Shader的使用
- 首先编写Shader,之后编译Shader,建立program集合所有的Shader,再做program的链接,之后渲染
- Shader的代码实质上就是字符串,别无而异同
Vertex Shader
- 世界坐标系:使用OpenGL来说明,又称之为右手笛卡尔坐标系统,用来描述物体与光源的位置
- 坐标变换矩阵栈(ModelView):用来存储一系列的变换矩阵,栈顶就是当前坐标的变换矩阵,进入OpenGL管道的每个坐标(齐次坐标)都会先乘上这个矩阵,结果才是对应点再场景中的世界坐标
图2-6 坐标变换过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 顶点着色器编写的两件事:顶点变换,告诉片段着色器当中需要插值的量 // 顶点着色器针对的是个体,因此不包括for循环 // 顶点位置,vec3:(x,y,z)坐标,attribute关键字:顶点属性 attribute vec3 aVertexPosition; // 顶点法线属性 attribute vec3 aNormalPosition; // 顶点纹理坐标 attribute vec2 aTextureCoord; // unifrom: 全局变量,直接从CPU扔到GPU中 //uModelViewMatrix:坐标变换矩阵栈 uniform mat4 uModelViewMatrix; // uProjectionMatrix:投影矩阵 uniform mat4 uProjectionMatrix; // varying: 声明顶点颜色变量,指定到Fragment shader中有相同的定义 // highp:32位浮点数格式,适用于顶点变换,但是性能最慢,放置再数据类型前 varying highp vec2 vTextureCoord; varying highp vec3 vFragPos; varying highp vec3 vNormal; void main(void) { vFragPos = aVertexPosition; vNormal = aNormalPosition;
gl_position = uProjectionMatrix * uModelViewMatrix *vec4(aVertexPosition,1.0); vTextureCoord = aTexureCoord; }
|
Fragment Shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| // sampler2D:用于对2D纹理进行采样,通常针对位图 uniform sampler2D uSampler; // 外部传入量 uniform vec3 uKd; uniform vec3 uKs; uniform vec3 uLightPos; uniform vec3 uCameraPos; uniform float uLightIntensity; uniform int uTextureSample;
// Vertex Shader传入量 varying highp vec2 vTextureCoord; varying highp vec3 vFragPos; varying highp vec3 vNormal;
void main(void) { vec3 color; if (uTextureSample == 1) { // texture2D(tex, vTextureCoord):sample the texture // vec4类型值.rgb==>vec3 // 2.2次幂:伽马畸变而需要 color = pow(texture2D(uSampler, vTextureCoord).rgb, vec3(2.2)); } else { color = uKd; }
// 简单的Phong模型 vec3 ambient = 0.05 * color; vec3 lightDir = normalize(uLightPos - vFragPos); vec3 normal = normalize(vNormal); float diff = max(dot(lightDir, normal), 0.0); float light_atten_coff = uLightIntensity / length(uLightPos - vFragPos); vec3 diffuse = diff * light_atten_coff * color; vec3 viewDir = normalize(uCameraPos - vFragPos); float spec = 0.0; vec3 reflectDir = reflect(-lightDir, normal); spec = pow(max(dot(viewDir, reflectDir), 0.0), 35.0); vec3 specular = uKs * light_atten_coff * spec; // gl_FragColor是Fragment Shader输出,1.0/2.2次幂防止伽马畸变 gl_FragColor = vec4(pow((ambient + diffuse + specular), vec3(1.0 / 2.2)), 1.0); }
|
- Nsight Graphics(跨平台,只适用于NVIDA GPUs)
- RenderDoc(跨平台,没有GPUs限制)
- SHADERed
- RGB调试法:同样将values视作颜色,使用color picker工具进行
编写GLSL的工具
- HLSL: 可以使用Visual Studio
- GLSL: VSCode+WebGL GLSL Editor插件
2-4 Rendering Equation(Most important equation in rendering)
- Describing light transport
- One-bounce global illumation
- Two-bounce global illumation
版权保护:以上所有的图片资源若侵则删之,若有错误请联系本人更正,欢迎读者批评指正
补充阅读资料:
参考文献: